package ltd.jdsoft.cute.token.helper;

import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Component;

import ltd.jdsoft.cute.common.util.PropertyUtil;
import ltd.jdsoft.cute.token.model.Token;
import ltd.jdsoft.cute.token.service.TokenService;

/**
 * 令牌工具类，包括鉴权、生成新令牌等。由于令牌只是操作单张表，因此暂不考虑事务问题，不放在manager包目录
 * 
 * @author zhengzhq
 *
 */
@Component
public class TokenHelper {

  private static Logger logger = LoggerFactory.getLogger(TokenHelper.class);

  // 默认都TTL为1年
  private static final long DEFAULT_TTL =
      PropertyUtil.getDefaultConfig("cute.cache.ttl", Long.class, 31536000000l);
  @Autowired
  private TokenService tokenService;

  /**
   * 刷新token，令牌存活时间默认1年
   * 
   * @param ownerId
   * @param type
   * @return
   */
  public String refreshToken(long ownerId, int type) {
    return this.refreshToken(ownerId, type, DEFAULT_TTL);
  }

  /**
   * 刷新令牌，适用于用户登录之后设置token等场景
   * 
   * @param token
   * @param ownerId
   * @param type
   * @param ttl
   * @return
   */
  public String refreshToken(long ownerId, int type, long ttl) {
    // 根据token查询对应的记录
    Token token = tokenService.getByOwnerAndType(ownerId, type);
    if (token == null) {
      token = new Token();
      token.setOwnerId(ownerId);
      token.setType(type);
    }
    // 令牌激活时间从当前开始计算
    token.setExpireTime(System.currentTimeMillis() + ttl);
    produceTokenInrecursive(token, token.getToken());

    return token.getToken();
  }

  private void produceTokenInrecursive(Token token, String oldToken) {
    while (true) {
      try {
        token.setToken(UUID.randomUUID().toString());
        if (token.getId() > 0) {
          tokenService.update(token, oldToken);
        } else {
          tokenService.add(token);
        }
        break;
      } catch (DuplicateKeyException e) {
        logger.error("produce token duplicate!");
      }
    }
  }

  /**
   * token认证，判断令牌与所有者是否一致
   * 
   * @param token
   * @param ownerId
   * @param type
   * @return
   */
  public boolean authenticate(String token, long ownerId, int type) {
    Token tokenObj = getTokenInfo(token, type);
    if (tokenObj == null) {
      return false;
    }

    // 当前token已失效
    if (tokenObj.getExpireTime() <= System.currentTimeMillis()) {
      return false;
    }

    return tokenObj.getOwnerId() == ownerId;
  }

  /**
   * 根据令牌和类型获取令牌信息，比如失效时间、
   * 
   * @param token
   * @param type
   * @return
   */
  public Token getTokenInfo(String token, int type) {
    Token tokenObj = tokenService.get(token);
    if (tokenObj == null) {
      return null;
    }

    if (tokenObj.getType() != type) {
      // 令牌类型和预期的不一致，是否使用有问题，或者是被盗用？告警日志记录下
      logger.warn("token type mismatch! query token '{}',expected type '{}', but was '{}'",
          new Object[] {token, type, tokenObj.getType()});
      return null;
    }

    return tokenObj;
  }
}
